package Task28;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;

public class Model {
	private CommandFactory factory = new CommandFactory();
	private ArrayList<String> errors = new ArrayList<String>();
	public final String operationCompleate = "Operation complete";
	public final String operationFailed = "Operation failed";
	public final String pathIsFile = "Path is file";

	private CommandInterface getInstance(String command) {
		CommandInterface instance = null;
		try {
			factory.createMap();
			if (factory.containsId(command))
				instance = factory.getCommandInstance(command);
			else
				errors.add("Unknown name of command.");
		} catch (Exception e) {
			errors.add(e.getMessage());
		}
		return instance;
	}

	public Result getResult(Command command) throws Exception {
		Result result = new Result();
		String id = command.getNameCommand();
		CommandInterface instance = getInstance(id);
		result.addExceptions(errors);
		if (errors.isEmpty()) {
			ArrayList<String> aResult = new ArrayList<String>();
			aResult = instance.getResult(command.getArgs());
			if (command.getNameCommand().equalsIgnoreCase("cd")) {
				result = cdSortResult(aResult);
			} else {
				result.addData(aResult);
			}
		}
		return result;
	}

	private Result cdSortResult(ArrayList<String> aResult) {
		Result result = new Result();
		File testFile = new File(aResult.get(0));
		if (testFile.exists())
			result.addData(aResult);
		else
			result.addErrors("Not found directory.");
		return result;
	}

	public class Copy extends CommandInterface {
		public static final String id = "copy";

		public Copy() {
		}

		@Override
		public ArrayList<String> getResult(String[] args) throws Exception {
			File pathFrom = new File(args[0]);
			File pathTo = new File(args[1]);
			boolean bResult;
			if (pathFrom.isFile())
				bResult = copyFile(pathFrom, pathTo);
			else
				bResult = copyDir(pathFrom, pathTo);
			ArrayList<String> result = new ArrayList<String>();
			if (bResult)
				result.add(operationCompleate);
			else
				result.add(operationFailed);
			return result;
		}

		private boolean copyFile(File fileFrom, File fileTo) throws IOException {
			boolean result = false;
			InputStream in = new FileInputStream(fileFrom);
			OutputStream out = new FileOutputStream(fileTo);
			byte[] buf = new byte[1024];
			int len;
			while ((len = in.read(buf)) > 0) {
				out.write(buf, 0, len);
			}
			in.close();
			out.close();
			result = true;
			return result;
		}

		private boolean copyDir(File fileFrom, File fileTo) throws IOException {
			// Copy all tree directory
			boolean result = fileTo.mkdir();

			if (fileFrom.list().length > 0) {
				String[] copyList = fileFrom.list();
				boolean flag = true;
				for (int i = 0; i < copyList.length; i++) {
					String sFileFromList = fileFrom.getAbsolutePath()
							+ File.separator + copyList[i];
					File fileFromList = new File(sFileFromList);
					if (fileFromList.isDirectory()) {
						File newFile = new File(fileTo.getAbsoluteFile()
								+ File.separator + fileFromList.getName());
						if (!copyDir(fileFromList, newFile))
							flag = false;
					} else {
						File newFile = new File(fileTo.getAbsoluteFile()
								+ File.separator + fileFromList.getName());
						if (!copyFile(fileFromList, newFile)) {
							flag = false;
							break;
						}
					}
				}
				// flag = true if all files were copy from directory
				result = flag;
			}
			return result;
		}
	}

	public class Move extends CommandInterface {
		public static final String id = "move";

		public Move() {
		}

		private Copy myCopy = new Copy();
		private Delete myDel = new Delete();

		@Override
		public ArrayList<String> getResult(String[] args) throws Exception {
			File pathFrom = new File(args[0]);
			File pathTo = new File(args[1]);
			boolean bResult;
			if (pathFrom.isFile()) {
				bResult = myCopy.copyFile(pathFrom, pathTo);
				if (bResult)
					bResult = myDel.delFile(pathFrom);
			} else {
				bResult = myCopy.copyDir(pathFrom, pathTo);
				if (bResult)
					bResult = myDel.delDir(pathFrom);
			}
			ArrayList<String> result = new ArrayList<String>();
			if (bResult)
				result.add(operationCompleate);
			else
				result.add(operationFailed);
			return result;
		}

	}

	public class Delete extends CommandInterface {
		public static final String id = "delete";

		public Delete() {
		}

		@Override
		public ArrayList<String> getResult(String[] args) {
			File pathDel = new File(args[0]);
			boolean bResult;
			if (pathDel.isFile())
				bResult = delFile(pathDel);
			else
				bResult = delDir(pathDel);
			ArrayList<String> result = new ArrayList<String>();

			if (bResult)
				result.add(operationCompleate);
			else
				result.add(operationFailed);
			return result;
		}

		private boolean delFile(File pathDel) {
			boolean bResult = false;
			if (pathDel.delete())
				bResult = true;
			else
				pathDel.deleteOnExit();
			return bResult;
		}

		private boolean delDir(File pathDel) {
			// Deleted all tree directory
			boolean result = false;
			if (pathDel.list().length > 0) {
				String[] delList = pathDel.list();
				boolean flag = true;
				for (int i = 0; i < delList.length; i++) {
					String sFileFromList = pathDel.getPath() + File.separator
							+ delList[i];
					File fileFromList = new File(sFileFromList);
					if (fileFromList.isDirectory()) {
						if (!delDir(fileFromList)) {
							flag = false;
							break;
						}
					} else {
						if (!delFile(fileFromList)) {
							flag = false;
							break;
						}
					}
				}
				// flag = true if all files were deleted from directory
				if (flag = true)
					result = delFile(pathDel);
			} else
				result = delFile(pathDel);
			return result;
		}

	}

	public class Dir extends CommandInterface {
		public static final String id = "dir";

		public Dir() {
		}

		@Override
		public ArrayList<String> getResult(String[] args) {
			ArrayList<String> list = new ArrayList<String>();

			File f = new File(args[0]);
			String[] dirList = f.list();

			int k = 0;
			String[] fileList = new String[dirList.length];

			for (int i = 0; i < dirList.length; i++) {
				File f1 = new File(args[0] + File.separator + dirList[i]);
				if (!f1.isFile()) {
					list.add(dirList[i]);
				} else {
					fileList[k] = dirList[i];
					k++;
				}
			}
			for (int i = 0; i < k; i++) {
				list.add(fileList[i]);
			}
			return list;
		}

	}

	public class Cd extends CommandInterface {
		public static final String id = "cd";

		private int numberPaths;

		public Cd() {
		}

		@Override
		public ArrayList<String> getResult(String[] args) {
			// throws DirectoryIsRootException, MyFileNotFoundException {
			// String newDir = arg1;
			String directory = args[0];

			String[] arrayPath = getArray(directory);

			for (int i = numberPaths; i >= 0; i--) {
				directory = changeDir(directory, arrayPath[i]);
			}
			ArrayList<String> aResult = new ArrayList<String>();
			aResult.add(directory);
			return aResult;
		}

		private boolean dirIsRoot(File fDir) {
			// Check if work directory is root.
			boolean bDirIsRoot = false;
			File[] arrayRoots = File.listRoots();
			for (int i = 0; i < arrayRoots.length; i++) {
				if (arrayRoots[i].equals(fDir))
					bDirIsRoot = true;
			}
			return bDirIsRoot;
		}

		private String[] getArray(String directory) {
			File fDir = new File(directory);
			String[] arrayDir = new String[directory.length()];
			int i = 0;

			while (!dirIsRoot(fDir)) {
				arrayDir[i] = fDir.getName();
				i++;
				fDir = fDir.getParentFile();
				if (fDir == null)
					break;
			}

			if (fDir != null) {
				arrayDir[i] = fDir.toString();
				numberPaths = i;
			} else
				numberPaths = i - 1;

			return arrayDir;
		}

		public String changeDir(String directory, String newDir) {
			// throws DirectoryIsRootException, MyFileNotFoundException {
			File fDir = new File(directory);

			if (newDir.equals("..")) {
				if (!dirIsRoot(fDir))
					directory = fDir.getParent();
				else {
					// throw new DirectoryIsRootException();
				}
			} else {
				File fArg = new File(newDir);
				if (!fArg.exists())
					newDir = directory + File.separator + newDir;
				File fNewDir = new File(newDir);
				if (fNewDir.exists())
					directory = newDir;
				else {
					// throw new MyFileNotFoundException();
				}
			}
			return directory;
		}
	}

	public class MkDir extends CommandInterface {
		public static final String id = "mkdir";

		public MkDir() {
		}

		@Override
		public ArrayList<String> getResult(String[] args) {
			String sNewDir = args[0];
			File newDir = new File(sNewDir);
			boolean bResult = newDir.mkdirs();
			ArrayList<String> aResult = new ArrayList<String>();
			if (bResult)
				aResult.add(operationCompleate);
			else
				aResult.add(operationFailed);
			return aResult;
		}

	}

	public class Tree extends CommandInterface {
		public static final String id = "tree";
		private ArrayList<String> list;

		public Tree() {
		}

		@Override
		public ArrayList<String> getResult(String[] args) {
			list = new ArrayList<String>();
			insertedTree(args[0], 0);
			return list;
		}

		private void printNameDir(String directory, String dirList, int y) {
			File f1 = new File(directory + File.separator + dirList);
			String printString = "";
			if (!f1.isFile()) {
				for (int j = 0; j < y; j++)
					printString += "    ";
				printString += "[+] " + dirList;
				list.add(printString);
				printString = "";
			} else {
				for (int j = 0; j < y; j++)
					printString += "    ";
				printString += "    " + dirList;
				list.add(printString);
				printString = "";
			}
		}

		private void insertedTree(String directory, int y) {
			// y - level inserted. Start from 0. Increase.
			File f = new File(directory);
			String[] dirList = f.list();
			for (int i = 0; i < dirList.length; i++) {
				printNameDir(directory, dirList[i], y);
				// if dirList[i] is directory then call printTree for dirList[i]
				String absDir = directory + File.separator + dirList[i];
				File f1 = new File(absDir);
				if (!f1.isFile())
					insertedTree(absDir, y + 1);
			}
		}

	}

	public class Find extends CommandInterface {
		public static final String id = "find";

		public Find() {
		}

		@Override
		public ArrayList<String> getResult(String[] args) {
			list = new ArrayList<String>();
			searchInTreeRecursion(args[0], args[1]);
			return list;
		}

		private ArrayList<String> list;

		private void searchInTreeRecursion(String directory, String mask) {
			// Method runs searchInDir only in folder - 'dir' and run itself in
			// all include folders
			File f = new File(directory);
			searchInDir(directory, mask);
			String[] dirList = f.list();
			for (int i = 0; i < dirList.length; i++) {
				String absDir = directory + File.separator + dirList[i];
				File f1 = new File(absDir);
				if (!f1.isFile())
					searchInTreeRecursion(absDir, mask);
			}
		}

		public void searchInDir(String directory, String mask) {
			File f = new File(directory);
			String[] dirList = f.list();

			if (mask.contains("*"))
				mask = mask.replace("*", "`");
			if (mask.contains("`")) {
				String[] arrayMask;
				arrayMask = mask.split("`");

				for (int i = 0; i < dirList.length; i++) {
					String sDirList = dirList[i];
					int k = 0;
					while (sDirList.length() > 0 && k < arrayMask.length) {
						if (sDirList.startsWith(arrayMask[k])) {
							for (int r = 0; r < arrayMask[k].length(); r++)
								sDirList = cutDir(sDirList);
							k++;
						} else {
							sDirList = cutDir(sDirList);
						}
					}
					if (k >= arrayMask.length)

						printName(directory + File.separator + dirList[i]);
				}

			} else {
				for (int i = 0; i < dirList.length; i++) {
					if (dirList[i].equals(mask))
						printName(directory + File.separator + dirList[i]);
				}
			}
		}

		private String cutDir(String directory) {
			int ld = directory.length();
			char[] newDir = new char[ld - 1];
			directory.getChars(1, ld, newDir, 0);
			String sNewDir = newDir.toString();
			sNewDir = String.copyValueOf(newDir);
			return sNewDir;
		}

		private void printName(String dirName) {
			File f1 = new File(dirName);
			Date date = new Date(f1.lastModified());
			list.add(f1.getName() + " - " + date + " - " + f1.getPath());
		}

	}

	public class Exit extends CommandInterface {
		public static final String id = "exit";

		public Exit() {
		}

		@Override
		public ArrayList<String> getResult(String[] args) {
			System.exit(0);
			return null;
		}

	}

	public class Open extends CommandInterface {
		public static final String id = "open";

		public Open() {
		}

		@Override
		public ArrayList<String> getResult(String[] args) throws Exception {
			Runtime.getRuntime().exec("notepad.exe " + args[0]);
			ArrayList<String> result = new ArrayList<String>();
			File file = new File(args[0]);
			result.add(file.getParent());
			return result;
		}
	}

	public class XML extends CommandInterface {
		public static final String id = "xml";

		public XML() {
		}

		@Override
		public ArrayList<String> getResult(String[] args) throws Exception {
			MySAXParser myParser =new MySAXParser(args[0]);
			ArrayList<String> result=new ArrayList<String>();
			result=myParser.getResult();
			return result;
		}

	}

}
